package com.up.remoting.service.impl;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.util.EntityUtils;

import com.google.gson.Gson;
import com.up.remoting.Constants;
import com.up.remoting.service.RemotingService;
import com.up.remoting.util.TypeUtils;

/**
 * Abstract remoting service.
 * 
 * @author xueqiang.mi
 * 
 */
public abstract class AbstractRemotingService implements RemotingService {

  @Override
  public String get(String uri) {
    HttpClient client = getHttpClientCookied();
    HttpGet request = new HttpGet(uri);

    try {
      HttpResponse response = client.execute(request);
      if (response.getStatusLine().getStatusCode() == 200) {
        return EntityUtils.toString(response.getEntity());
      } else {
        return String.valueOf(response.getStatusLine().getStatusCode());
      }
    } catch (Exception e) {
      return e.getMessage();
    }
  }

  @Override
  public Map<String, String> getUser() {
    String data = get(Constants.SDK_API_USER_CURRENT_USER);
    return new Gson().fromJson(data, TypeUtils.TYPE_MAP_STRING_STRING);
  }

  @Override
  public boolean loggedIn() {
    HttpGet request = new HttpGet(Constants.SDK_API_USER_LOGGED_IN);

    try {
      DefaultHttpClient client = getHttpClientCookied();
      HttpResponse response = client.execute(request);

      if ("true".equals(EntityUtils.toString(response.getEntity()))) {
        return true;
      }
    } catch (Exception e) {
      // Ignore it.
    }

    return false;
  }

  /**
   * Login and save cookie locally.
   * 
   * @param email
   *          user email
   * @param password
   *          password
   * @return <true> if login OK.
   */
  public final boolean login(final String email, final String password) {
    List<BasicNameValuePair> nameValuePairs = new ArrayList<BasicNameValuePair>();
    nameValuePairs.add(new BasicNameValuePair("email", email));
    nameValuePairs.add(new BasicNameValuePair("password", password));

    try {
      UrlEncodedFormEntity entity = new UrlEncodedFormEntity(nameValuePairs, "utf-8");
      entity.setContentType("application/x-www-form-urlencoded");
      HttpPost request = new HttpPost(Constants.SDK_API_USER_LOGIN);
      request.setEntity(entity);

      DefaultHttpClient client = getHttpClient();
      HttpResponse response = client.execute(request);

      String respStr = EntityUtils.toString(response.getEntity());
      if ("true".equals(respStr)) {
        storeCookie(client.getCookieStore());
        return true;
      }
    } catch (Exception e) {
      // Ignore it.
    }

    return false;
  }

  @Override
  public boolean logout() {
    HttpGet request = new HttpGet(Constants.SDK_API_USER_LOGOUT);

    try {
      DefaultHttpClient client = getHttpClientCookied();
      HttpResponse response = client.execute(request);

      String respStr = EntityUtils.toString(response.getEntity());
      if ("true".equals(respStr)) {
        return true;
      }
    } catch (Exception e) {
      // Ignore it.
    }

    return false;
  }

  /**
   * @return {@link HttpClient} instance.
   */
  protected final DefaultHttpClient getHttpClient() {
    DefaultHttpClient httpClient = new DefaultHttpClient();
    HttpParams params = httpClient.getParams();
    params.setParameter(CoreProtocolPNames.USER_AGENT, "Android HttpClient Agent");
    params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);

    // Set time out.
    HttpConnectionParams.setConnectionTimeout(params, 10000);
    HttpConnectionParams.setSoTimeout(params, 10000);

    return httpClient;
  }

  /**
   * @return {@link HttpClient} instance with cookie.
   */
  protected final DefaultHttpClient getHttpClientCookied() {
    DefaultHttpClient httpClient = getHttpClient();
    CookieStore cookieStore = httpClient.getCookieStore();

    try {
      cookieStore.addCookie(readCookie());
    } catch (Exception e) {
      e.printStackTrace();
      // Ignore it.
    }

    return httpClient;
  }

  protected abstract String getSessionId();

  protected abstract void storeSessionId(String sessionId);

  private Cookie readCookie() throws IOException {
    BasicClientCookie cookie = new BasicClientCookie("session", getSessionId());
    cookie.setDomain(Constants.COOKIE_DOMAIN);
    cookie.setPath("/");
    cookie.setSecure(false);
    cookie.setVersion(0);
    cookie.setAttribute("domain", Constants.COOKIE_DOMAIN);
    cookie.setAttribute("path", "/");
    return cookie;
  }

  private void storeCookie(CookieStore cookieStore) throws IOException {
    String sessionId = null;
    for (Cookie cookie : cookieStore.getCookies()) {
      if ("session".equals(cookie.getName())) {
        sessionId = cookie.getValue();
      }
    }

    if (sessionId != null) {
      storeSessionId(sessionId);
    }
  }

}
